package tpoo.common.utils;

import static tpoo.common.services.utils.ServiceConstants.CODEC_SERVICE;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Observable;
import java.util.Observer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import tpoo.common.services.utils.ServiceAdmin;
import tpoo.common.utils.codec.Codec;
import tpoo.common.utils.passiveOperations.PassiveOperation;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * Implementa una suerte de observer remoto
 * Se ejecuta en el cliente, escucha envios del servidor
 * 
 * @author diemar
 * 
 */
public class Listener extends Observable {

	private static final String CODE_SEPARATOR = ":";
	private Integer port;
	private Boolean stop = false;
	
	private static Log logger = LogFactory.getLog(Listener.class);

	private static Listener listener;

	public static Listener getListener() {
		if (listener == null)
			listener = new Listener(0);
		return listener;
	}

	private Listener(Integer port) {
		this.port = port;
	}

	public void start() {
		new Thread(new Runnable() {
			public void run() {
				Parameters parameters;
				ServerSocket ss = null;
				try {
					ss = new ServerSocket(port);
					setPort(ss.getLocalPort());
					logger.info("Escuchando en el puerto " + getPort());
					while (!stop) {
						try {
							logger.debug("Esperando conexiones...");
							Socket client = ss.accept();
							logger.debug("Conexión desde: "
									+ client.getInetAddress());
							
							Codec codec= (Codec) ServiceAdmin.getService(CODEC_SERVICE);
							
							PassiveOperation pop = (PassiveOperation) codec.decode(client.getInputStream());
							launchPassiveOperation(pop);
							
							setChanged();
							notifyObservers();
						} catch (Exception e) {
							//Para que el Listener siga "vivo" ante cualquier error en la transferencia de operaciones
							e.printStackTrace();
						}
					}
					logger.debug("Thread-listener terminado.");
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					try {
						if (ss != null) {
							logger.debug("Cerrando el socket del cliente");
							ss.close();
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}

			
			private void launchPassiveOperation(final PassiveOperation pop) {
				try {
					logger.debug("Ejecutando "+pop);
					pop.execute();
				} catch (Exception e) {
					logger.error("Ejecutando una operación pasiva",
							e);
				}
			}


			private void __launchPassiveOperation(final PassiveOperation pop) {
				try {
					Thread t=new Thread(new Runnable() {
						public void run() {
							try {
								logger.debug("Ejecutando "+pop);
								pop.execute();
							} catch (Exception e) {
								logger.error("Ejecutando una operación pasiva",
										e);
							}
						}
					});
					t.setDaemon(true);
					t.start();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void stop() {
		this.stop = true;
	}

	public Integer getPort() {
		return port;
	}

	private void setPort(Integer port) {
		this.port = port;
	}

	public InetAddress getIp() {
		try {
			return InetAddress.getLocalHost();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	protected void finalize() throws Throwable {
		logger.debug("Bajando el Listener");
		this.stop();
		super.finalize();
	}

	@Override
	public synchronized void addObserver(Observer o) {
		logger.debug("Agregando el observer " + o + " a " + this);
		super.addObserver(o);
	}
}
